/*
 * qrlocation.cpp
 * 二维码定位和识别模块
 * 功能：
 * 1. 图像预处理（边缘检测、二值化等）
 * 2. 二维码定位
 * 3. 透视变换校正
 * 4. 图像增强
 * 输出：处理后的图像保存在 Dst_image 目录
 */

#pragma once
#include <opencv2/opencv.hpp>
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>

using namespace std;
using namespace cv;

Mat src_gray;
Mat dst, detected_edges;
int lowThreshold = 80;
int ratio1 = 3;
int kernel_size = 3;
int highThreshold = lowThreshold * ratio1;
int idx = 0;
Mat temp;

Mat DetectEdge(Mat& src);//ڱԵ̽⣬Զάİɫ߿̽
Mat Binarization(Mat src);
void ErodeAndDilate(Mat& src, Mat& dst);
vector<Point> CutPoly(Mat& src, Mat& dst);
Mat PerspectTrans(Mat src, vector<Point> point);
vector<Mat> QRcode(vector<Mat>& src_ims);


Mat Binarization(Mat src) {
    for (int i = 0; i < src.rows; i++) {
        for (int j = 0; j < src.cols; j++) {
            src.ptr<uchar>(i)[j] = (src.ptr<uchar>(i)[j] > 128 ? 255 : 0);
        }
    }
    return src;
}

Mat DetectEdge(Mat& src)
{
    bilateralFilter(src_gray, detected_edges, 5, 200, 200, BORDER_REFLECT_101);
    Canny(detected_edges, detected_edges, lowThreshold, highThreshold, kernel_size);
    dst = Scalar::all(0);
    src.copyTo(dst, detected_edges);
    return dst;
}

void ErodeAndDilate(Mat& src, Mat& dst)
{
    Mat element = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));
    dilate(src, dst, element);
    erode(dst, dst, element);
    for (int i = 0; i < 8; i++)
    {
        dilate(dst, dst, element);
        erode(dst, dst, element);
    }
}

vector<Point> CutPoly(Mat& src, Mat& dst)
{
    double maxarea = 0;
    int maxAreaIdx = 0;
    vector<vector<Point>>contours;
    vector<Vec4i>hierarchy;
    findContours(src, contours, hierarchy, cv::RETR_TREE, cv::CHAIN_APPROX_SIMPLE, Point(0, 0));
    for (int i = 0; i < contours.size(); i++)
    {
        double tmparea = fabs(contourArea(contours[i]));
        if (tmparea > maxarea)
        {
            maxarea = tmparea;
            maxAreaIdx = i;
            continue;
        }
    }
    vector<Point>vertex_point;
    approxPolyDP(contours[maxAreaIdx], vertex_point, 50, 1);
    return vertex_point;
}

Mat PerspectTrans(Mat src, vector<Point> point)
{
    Point2f SrcPoints[4];
    Point2f DstPoints[4];

    bool x[4] = { false,false,false,false };
    bool y[4] = { false,false,false,false };
    float x_tmp1;
    float x_tmp2;
    int idx11;
    int idx12;
    if (point[0].x < point[1].x)
    {
        x_tmp1 = point[0].x;
        idx11 = 0;
        x_tmp2 = point[1].x;
        idx12 = 1;
    }
    else
    {
        x_tmp1 = point[1].x;
        idx11 = 1;
        x_tmp2 = point[0].x;
        idx12 = 0;
    }

    for (int i = 2; i < 4; i++)
    {
        if (point[i].x < x_tmp2)
        {
            if (point[i].x < x_tmp1)
            {
                x_tmp2 = x_tmp1;
                idx12 = idx11;
                x_tmp1 = point[i].x;
                idx11 = i;
            }
            else
            {
                x_tmp2 = point[i].x;
                idx12 = i;
            }
        }
    }
    x[idx11] = true;
    x[idx12] = true;
    float y_tmp1;
    float y_tmp2;
    int idx21;
    int idx22;
    if (point[0].y < point[1].y)
    {
        y_tmp1 = point[0].y;
        idx21 = 0;
        y_tmp2 = point[1].y;
        idx22 = 1;
    }
    else
    {
        y_tmp1 = point[1].y;
        idx21 = 1;
        y_tmp2 = point[0].y;
        idx22 = 0;
    }

    for (int i = 2; i < 4; i++)
    {
        if (point[i].y < y_tmp2)
        {
            if (point[i].y < y_tmp1)
            {
                y_tmp2 = y_tmp1;
                idx22 = idx21;
                y_tmp1 = point[i].y;
                idx21 = i;
            }
            else
            {
                y_tmp2 = point[i].y;
                idx22 = i;
            }
        }
    }
    y[idx21] = true;
    y[idx22] = true;

    for (int i = 0; i < 4; i++)
    {
        if (x[i] && y[i])
        {
            SrcPoints[0].x = point[i].x;
            SrcPoints[0].y = point[i].y;
        }
        else if (x[i] && !y[i])
        {
            SrcPoints[1].x = point[i].x;
            SrcPoints[1].y = point[i].y;
        }
        else if (!x[i] && !y[i])
        {
            SrcPoints[2].x = point[i].x;
            SrcPoints[2].y = point[i].y;
        }
        else
        {
            SrcPoints[3].x = point[i].x;
            SrcPoints[3].y = point[i].y;
        }
    }
    DstPoints[0] = Point(0, 0);
    DstPoints[1] = Point(0, 1000);
    DstPoints[2] = Point(1000, 1000);
    DstPoints[3] = Point(1000, 0);

    Mat warpmatrix = getPerspectiveTransform(SrcPoints, DstPoints);

    Mat res;

    warpPerspective(src, res, warpmatrix, res.size(), INTER_LANCZOS4);

    Rect res_rect(0, 0, 1000, 1000);
    res = res(res_rect);

    return res;
}

vector<Mat> QRcode(vector<Mat>& Images)
{
    vector<Mat> src;
    for (auto image : Images)
    {
        if (!image.data)
            exit(-1);
        resize(image, image, Size(1000, 1000), 0, 0, INTER_LANCZOS4);
        imshow("src", image);

        temp.create(image.size(), image.type());
        cvtColor(image, src_gray, cv::COLOR_BGR2GRAY);
        temp = DetectEdge(image);
        cvtColor(temp, temp, cv::COLOR_BGR2GRAY);
        ErodeAndDilate(temp, temp);
        vector<Point>point = CutPoly(temp, temp);
        image = PerspectTrans(image, point);
        cvtColor(image, image, cv::COLOR_BGR2GRAY);
        idx++;
        cout << idx << ": " << endl << image.rows << " " << image.cols << endl;
        src.push_back(image);
        
    }
    return src;
}



void qrlocation() {
    vector<Mat> SrcImages;
    vector<Mat> DstImages;

    String picname = ".\\Scrimage\\*.jpg";

    vector<String>image_files;
    glob(picname, image_files);
    for (int i = 0; i < image_files.size(); i++)
    {
        Mat tmp = imread(image_files[i]);
        if (tmp.data)
            SrcImages.push_back(tmp);
    }

    DstImages = QRcode(SrcImages);

    for (int i = 0; i < DstImages.size(); i++)
    {
        DstImages[i] = Binarization(DstImages[i]);
        //bilateralFilter(dst_images[i], dst_images[i], 5, 200, 200, BORDER_REFLECT_101);
        GaussianBlur(DstImages[i], DstImages[i], Size(7, 7), 0);
        char str[10];
        sprintf_s(str, "%u.png", i + 1);
        string tmp = ".\\Dst_image\\";
        string strSaveName = tmp + str;
        cout << strSaveName << endl;
        imwrite(strSaveName, DstImages[i]);
    }
    cout << "!" << endl;
    waitKey(0);

    return;
}